package com.yeskery.nut.http;

import com.yeskery.nut.core.*;
import com.yeskery.nut.util.IOUtils;

import java.io.InputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 该类是 {@link Request} 的抽象实现，实现自定义 {@link Request} 时，可继承本类
 *
 * @author sprout
 * 2019-03-14 09:55
 * @version 1.0
 *
 * @see com.yeskery.nut.core.Request
 */
public abstract class BaseRequest extends BasicContextImpl<Object> implements Request, Serializable, Cloneable {

	/** 换行的字节 */
	public static final byte[] BOX_OFF = {13, 10};

	/** 查询参数标识符 */
	private static final String QUERY_SYMBOL = "?";

	/** Session 管理器 */
	private final transient SessionManager sessionManager;

	/** 服务上下文 */
	private final transient ServerContext serverContext;

	/** 服务请求配置对象 */
	private final ServerRequestConfiguration serverRequestConfiguration;

	/** 请求体字节数组 */
	protected transient byte[] body;

	/** 是否超出请求体限制大小 */
	protected volatile transient boolean isOverflowBodySize = false;

	/** 请求的请求头集合 */
	protected Map<String, List<String>> headers;

	/** 请求的参数集合 */
	protected Map<String, List<String>> parameters;

	/** 请求的查询参数集合 */
	protected Map<String, List<String>> queryParameters;

	/** 请求的方法 */
	protected Method method;

	/** 原始请求的路径 */
	protected String originalPath;

	/** 请求的路径 */
	protected String path;

	/** 请求的协议 */
	protected String protocol;

	/** 请求的请求体是否为空 */
	protected boolean empty;

	{
		headers = new HashMap<>();
		parameters =  new HashMap<>();
		queryParameters = new HashMap<>();
	}

	/**
	 * 构建一个 {@link BaseRequest}
	 * @param serverContext 服务上下文
	 * @param sessionManager Session 管理器
	 * @param serverRequestConfiguration 服务请求配置对象
	 */
	protected BaseRequest(ServerContext serverContext, SessionManager sessionManager, ServerRequestConfiguration serverRequestConfiguration) {
		this.serverContext = serverContext;
		this.sessionManager = sessionManager;
		this.serverRequestConfiguration = serverRequestConfiguration;
	}

	/**
	 * 请求初始化的方法，该方法会解析 Http 请求的数据，将请求对应的数据填充到对应的字段中
	 */
	protected void initialize() {
		byte[] data = readRequestBytes();
		if (data == null || data.length < 1) {
			empty = true;
			return;
		}
		int index = 0;
		boolean readBody = false;
		for (int i = 0; i < data.length; i++) {
			if (i <= data.length - 2 && data[i] == BOX_OFF[0] && data[i + 1] == BOX_OFF[1] && index == 0) {
				byte[] headerLineBytes = new byte[i];
				System.arraycopy(data, 0, headerLineBytes, 0, i);
				String headerLine = new String(headerLineBytes, StandardCharsets.UTF_8);
				String[] headerLineValues = headerLine.split("\\s");
				if (headerLineValues.length < 3) {
					throw new NutException("Http IO Error.");
				}
				method = Method.valueOf(headerLineValues[0].toUpperCase());
				path = headerLineValues[1];
				originalPath = path;
				protocol = headerLineValues[2];
				initUriParameters();
				index = i;
				continue;
			}
			if (i <= data.length - 4 && data[i] == BOX_OFF[0] && data[i + 1] == BOX_OFF[1]) {
				if (data[i + 2] == BOX_OFF[0] && data[i + 3] == BOX_OFF[1]) {
					body = new byte[data.length - i - (BOX_OFF.length * 2)];
					if (body.length > 0) {
						System.arraycopy(data, i + (BOX_OFF.length * 2), body, 0, body.length);
						readBody = true;
					}
				}
				byte[] headerLineBytes = new byte[i - index - BOX_OFF.length];
				System.arraycopy(data, index + BOX_OFF.length, headerLineBytes, 0, headerLineBytes.length);
				String header = new String(headerLineBytes, StandardCharsets.UTF_8);
				if (!header.contains(":")) {
					continue;
				}
				int pos = header.indexOf(":");
				List<String> headValues = Arrays.stream(header.substring(pos + 1).trim().split(";"))
						.map(String::trim).collect(Collectors.toList());
				headers.put(header.substring(0, pos).trim(), headValues);
				index = i;
				if (readBody) {
					break;
				}
			}
		}
	}

	/**
	 * 初始化uri参数
	 */
	protected void initUriParameters() {
		if (path.contains(QUERY_SYMBOL)) {
			int pos = path.indexOf(QUERY_SYMBOL);
			String parameterValues = path.substring(pos + 1);
			path = path.substring(0, pos);
			for (Map.Entry<String, List<String>> entry : getUriParameters(parameterValues).entrySet()) {
				queryParameters.put(entry.getKey(), entry.getValue());
				parameters.put(entry.getKey(), entry.getValue());
			}
		}
	}

	/**
	 * 获取uri参数集合
	 * @param uri uri参数
	 * @return uri参数集合
	 */
	protected Map<String, List<String>> getUriParameters(String uri) {
		String[] tempParameterValues = uri.split("&");
		Map<String, List<String>> valuesMap = new HashMap<>(tempParameterValues.length);
		for (String parameterValue : tempParameterValues) {
			String[] parameterValueArray = parameterValue.split("=");
			if (parameterValueArray.length < 2) {
				continue;
			}
			valuesMap.computeIfAbsent(parameterValueArray[0].trim(), k -> new ArrayList<>()).add(parameterValueArray[1].trim());
		}
		return valuesMap;
	}

	/**
	 * 从输入流中读取请求体字节数组
	 * @param inputStream 输入流
	 * @return 请求体字节数组
	 */
	protected byte[] readRequestBytesByInputStream(InputStream inputStream) {
		try {
			Long maxRequestSize = serverRequestConfiguration.getMaxRequestSize();
			return maxRequestSize == null ? IOUtils.readByteArray(inputStream)
					: IOUtils.readByteArray(inputStream, 8192, maxRequestSize,
					() -> new HttpRequestBodySizeOverflowException("Request size exceeds the maximum limit[" + maxRequestSize + "]."));
		} catch (Throwable e) {
			if (e instanceof HttpRequestBodySizeOverflowException) {
				isOverflowBodySize = true;
				return new byte[0];
			} else {
				throw new NutException("Jakarta Servlet InputStream IO Exception.", e);
			}
		}
	}

	/**
	 * 检查是否超出请求大小限制
	 * @param bodyLength 请求头大小
	 * @return 是否超出请求大小限制
	 */
	protected boolean checkRequestBodySizeOverflow(long bodyLength) {
		Long maxRequestSize = serverRequestConfiguration.getMaxRequestSize();
		if (maxRequestSize != null) {
			if (maxRequestSize < 0) {
				return isOverflowBodySize;
			}
			if (maxRequestSize == 0 || bodyLength > maxRequestSize) {
				isOverflowBodySize = true;
			}
		}
		return isOverflowBodySize;
	}

	@Override
	public List<String> getHeaders(String key) {
		return headers.get(key);
	}

	@Override
	public Map<String, List<String>> getHeaders() {
		return headers;
	}

	@Override
	public Set<String> getParameterKeys() {
		return parameters.keySet();
	}

	@Override
	public Cookie[] getCookies() {
		List<String> cookieValues =  headers.get(HttpHeader.COOKIE);
		if (cookieValues == null || cookieValues.isEmpty()) {
			return new Cookie[0];
		}
		List<Cookie> cookies = new LinkedList<>();
		for (String cookieValue : cookieValues) {
			String[] splits = cookieValue.split(";");
			for (String split : splits) {
				String[] cookieValueSplits = split.split("=");
				if (cookieValueSplits.length < 2) {
					continue;
				}
				cookies.add(new BasicCookie(cookieValueSplits[0].trim(), cookieValueSplits[1].trim()));
			}
		}
		return cookies.toArray(new Cookie[0]);
	}

	@Override
	public List<MultipartFile> getFiles(String key) {
		throw new NutException("UnSupport MultipartFile.");
	}

	@Override
	public byte[] getBody() {
		return body;
	}

	@Override
	public Map<String, List<String>> getParametersMap() {
		return parameters;
	}

	@Override
	public Map<String, List<String>> getQueryParametersMap() {
		return queryParameters;
	}

	@Override
	public Session getSession() {
		return sessionManager.getSession(sessionManager.getSessionId(this));
	}

	@Override
	public ServerContext getServerContext() {
		return serverContext;
	}

	@Override
	public boolean isEmpty() {
		return empty;
	}

	@Override
	public Method getMethod() {
		return method;
	}

	@Override
	public String getOriginalPath() {
		return originalPath;
	}

	@Override
	public String getPath() {
		return path;
	}

	@Override
	public String getProtocol() {
		return protocol;
	}

	@Override
	public ServerRequestConfiguration getServerRequestConfiguration() {
		return serverRequestConfiguration;
	}

	/**
	 * 是否超出请求大小限制
	 * @return 是否超出请求大小限制
	 */
	public boolean isOverflowRequestBodySize() {
		return isOverflowBodySize;
	}

	@Override
	public BaseRequest clone() {
		try {
			return (BaseRequest) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new NutException(e);
		}
	}

	/**
	 * 转换InetSocketAddress对象
	 * @param socketAddress socket地址
	 * @return InetSocketAddress对象
	 */
	protected InetSocketAddress covertInetSocketAddress(SocketAddress socketAddress) {
		if (!(socketAddress instanceof InetSocketAddress)) {
			throw new NutException("Socket Address Obtain Fail.");
		}
		return (InetSocketAddress) socketAddress;
	}

	/**
	 * 读取请求中的字节数组
	 * @return 请求中的字节数组
	 */
	protected abstract byte[] readRequestBytes();
}
